package com.flow.asyncflow.core.utils.http;

import cn.hutool.core.lang.UUID;
import lombok.SneakyThrows;
import org.apache.commons.lang.StringUtils;
import org.apache.http.*;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.CookieStore;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.cookie.Cookie;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.BasicCredentialsProvider;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.cookie.BasicClientCookie;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.util.EntityUtils;
import org.springframework.stereotype.Component;

import javax.net.ssl.SSLContext;
import java.io.InputStream;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.*;

/**
 * @author 1209785
 * @since 2023-3-8
 */
@Component
public class HttpClientUtil {

    private static CloseableHttpClient apacheHttpClient(CookieStore cookieStore) {
        return HttpClients.custom()
                .setDefaultCookieStore(cookieStore)
                .build();
    }


    @SneakyThrows
    public static HttpClientResponse httpResponse(HttpClientRequest httpClientRequest) {
        HttpClientResponse httpClientResponse = new HttpClientResponse();
        CookieStore cookieStore = new BasicCookieStore();
        addCookie(cookieStore, httpClientRequest.getCookieStore());
        //忽略证书
        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, (x509Certificates, s) -> true).build();
        CredentialsProvider provider = new BasicCredentialsProvider();
        //设置代理
        if (httpClientRequest.isProxy()) {
            HttpHost httpHost = new HttpHost(httpClientRequest.getProxyInfo().getHostName(), httpClientRequest.getProxyInfo().getPort());
            //包含账号密码的代理
            provider.setCredentials(new AuthScope(httpHost), new UsernamePasswordCredentials(httpClientRequest.getProxyInfo().getUserName(), httpClientRequest.getProxyInfo().getPassWord()));
        }

        try (CloseableHttpClient httpClient = HttpClients.custom()
                .setDefaultCookieStore(cookieStore)
                .setSSLContext(sslContext)
                .setDefaultCredentialsProvider(provider)
                .build()) {
            CloseableHttpResponse apacheHttpResponse = null;
            if (httpClientRequest.getHttpMethod() == HttpMethod.Get) {
                HttpGet httpGet = apacheHttpGet(httpClientRequest);
                apacheHttpResponse = httpClient.execute(httpGet);
            } else if (httpClientRequest.getHttpMethod() == HttpMethod.PostForm
                    || httpClientRequest.getHttpMethod() == HttpMethod.PostJson
                    || httpClientRequest.getHttpMethod() == HttpMethod.PostFile) {
                HttpPost httpPost = apacheHttpPost(httpClientRequest);
                apacheHttpResponse = httpClient.execute(httpPost);
            } else if (httpClientRequest.getHttpMethod() == HttpMethod.Put) {
                HttpPut httpPut = apacheHttpPut(httpClientRequest);
                apacheHttpResponse = httpClient.execute(httpPut);
            }
            //5.获取响应内容
            assert apacheHttpResponse != null;
            HttpEntity httpEntity = apacheHttpResponse.getEntity();
            String html = EntityUtils.toString(httpEntity, httpClientRequest.getCharset());
            httpClientResponse.setBody(html);
            httpClientResponse.setCode(apacheHttpResponse.getStatusLine().getStatusCode());
            //4.判断响应状态为200，进行处理
            if (apacheHttpResponse.getStatusLine().getStatusCode() == HttpStatus.SC_MOVED_TEMPORARILY) {
                //跳转的目标地址是在 HTTP-HEAD 中的
                Header header = apacheHttpResponse.getFirstHeader("location");
                String newUrl = header.getValue(); // 这就是跳转后的地址，再向这个地址发出新申请，以便得到跳转后的信息是啥。
                httpClientResponse.setDirectUrl(newUrl);
            }
            Header[] cookieStr = apacheHttpResponse.getHeaders("Set-Cookie");
            for (Header header : cookieStr) {
                String[] cookieInfoArr = header.getValue().split(";");
                String cookieName = "";
                String cookieValue = "";
                String cookiePathValue = "";
                if (cookieInfoArr.length > 0 && cookieInfoArr[0].contains("=") && cookieInfoArr[0].split("=").length == 2) {
                    String key = cookieInfoArr[0].split("=")[0];
                    String value = cookieInfoArr[0].split("=")[1];
                    cookieName = key;
                    cookieValue = value;
                }
                BasicClientCookie cookieHttp = new BasicClientCookie(cookieName, cookieValue);
                cookieHttp.setVersion(0);
                cookieHttp.setPath(cookiePathValue);
                cookieStore.addCookie(cookieHttp);
            }
            httpClientResponse.setCookieStore(cookieStore);
            httpClientResponse.setResponseHeaders(apacheHttpResponse.getAllHeaders());
        } catch (Exception ex) {
            System.out.println(Arrays.toString(ex.getStackTrace()));
        }


        return httpClientResponse;
    }


    private static void addCookie(CookieStore cookieStore, CookieStore thisCookieStore) {
        if (thisCookieStore == null || thisCookieStore.getCookies() == null || thisCookieStore.getCookies().size() == 0) {
            return;
        }
        for (Cookie cookie : thisCookieStore.getCookies()) {
            cookieStore.addCookie(cookie);

        }
    }

    private static HttpGet apacheHttpGet(HttpClientRequest httpClientRequest) {
        HttpGet requestGet = new HttpGet(httpClientRequest.getUrl());
        RequestConfig requestConfig = getRequestConfig(httpClientRequest);
        requestGet.setConfig(requestConfig);
        //设置请求头，将爬虫伪装成浏览器
        requestGet.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.169 Safari/537.36");
        if (httpClientRequest.getHeads() != null && httpClientRequest.getHeads().size() > 0) {
            for (String key : httpClientRequest.getHeads().keySet()) {
                requestGet.addHeader(key, httpClientRequest.getHeads().get(key));
            }
        }

        return requestGet;
    }


    @SneakyThrows
    private static HttpPost apacheHttpPost(HttpClientRequest httpClientRequest) {
        HttpPost requestPost = new HttpPost(httpClientRequest.getUrl());
        RequestConfig requestConfig = getRequestConfig(httpClientRequest);
        requestPost.setConfig(requestConfig);
        //设置请求头，将爬虫伪装成浏览器
        requestPost.addHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36");
        for (String key : httpClientRequest.getHeads().keySet()) {
            requestPost.addHeader(key, httpClientRequest.getHeads().get(key));
        }
        if (httpClientRequest.getHttpMethod() == HttpMethod.PostForm) {
            List<NameValuePair> params = new ArrayList<>();
            if (httpClientRequest.getParameter() != null && httpClientRequest.getParameter().size() > 0) {
                for (String key : httpClientRequest.getParameter().keySet()) {
                    params.add(new BasicNameValuePair(key, (String) httpClientRequest.getParameter().get(key)));
                }
            }

            //创建表单的Emtity对象，第一个参数是封装好的表单数据，第二个参数是编码
            UrlEncodedFormEntity formEntity = new UrlEncodedFormEntity(params, httpClientRequest.getCharset());
            requestPost.setEntity(formEntity);
        } else if (httpClientRequest.getHttpMethod() == HttpMethod.PostJson) {
            requestPost.setHeader("Content-Type", "application/json");
            StringEntity stringEntity = null;
            if (StringUtils.isNotBlank(httpClientRequest.getReqJson())) {
                stringEntity = new StringEntity(httpClientRequest.getReqJson(), "UTF-8");
            }
            requestPost.setEntity(stringEntity);
        } else if (httpClientRequest.getHttpMethod() == HttpMethod.PostFile) {
            MultipartEntityBuilder builder = MultipartEntityBuilder.create();
            builder.setCharset(StandardCharsets.UTF_8);
            //builder.setContentType(ContentType.MULTIPART_FORM_DATA);
            String boundary = "----WebKitFormBoundary" + System.currentTimeMillis() + UUID.randomUUID().toString();
            builder.setBoundary(boundary);
            requestPost.addHeader("Content-Type", "multipart/form-data;boundary=" + boundary);
            for (HttpClientRequest.FileInfo fileInfo : httpClientRequest.getFileInfoList()) {
                //转换成文件流
                InputStream is = new URL(fileInfo.getFileUrl()).openStream();
                /*绑定文件参数，传入文件流和contenttype，此处也可以继续添加其他formdata参数*/
                builder.addBinaryBody(StringUtils.isNotBlank(fileInfo.getFileProperty()) ? fileInfo.getFileProperty() : "file", is, ContentType.IMAGE_JPEG, fileInfo.getFileName());
            }
            for (Map.Entry<String, Object> map : httpClientRequest.getParameter().entrySet()) {
                builder.addTextBody(map.getKey(), (String) map.getValue(), ContentType.create("text/plain").withCharset("UTF-8"));
            }
            HttpEntity entity = builder.build();
            requestPost.setEntity(entity);
        }
        return requestPost;
    }

    @SneakyThrows
    private static HttpPut apacheHttpPut(HttpClientRequest httpClientRequest) {
        HttpPut requestPut = new HttpPut(httpClientRequest.getUrl());
        RequestConfig requestConfig = getRequestConfig(httpClientRequest);
        requestPut.setConfig(requestConfig);
        //设置请求头，将爬虫伪装成浏览器
        requestPut.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/110.0.0.0 Safari/537.36");

        for (String key : httpClientRequest.getHeads().keySet()) {
            requestPut.addHeader(key, httpClientRequest.getHeads().get(key));
        }
        StringEntity stringEntity = null;
        if (StringUtils.isNotBlank(httpClientRequest.getReqJson())) {
            stringEntity = new StringEntity(httpClientRequest.getReqJson(), ContentType.APPLICATION_JSON);
        }
        requestPut.setEntity(stringEntity);
        return requestPut;
    }

    @SneakyThrows
    private static RequestConfig getRequestConfig(HttpClientRequest httpClientRequest) {
        RequestConfig requestConfig = null;
        if (httpClientRequest.getRequestConfig() != null) {
            requestConfig = httpClientRequest.getRequestConfig();
        } else {
            RequestConfig.Builder builder = RequestConfig.custom()
                    //一、连接目标服务器超时时间：ConnectionTimeout-->指的是连接一个url的连接等待时间
                    .setConnectTimeout(50000)
                    //二、读取目标服务器数据超时时间：SocketTimeout-->指的是连接上一个url，获取response的返回等待时间
                    .setConnectionRequestTimeout(50000)
                    //三、从连接池获取连接的超时时间:ConnectionRequestTimeout
                    .setSocketTimeout(50000)
                    .setAuthenticationEnabled(true);
            if (httpClientRequest.isProxy()) {
                HttpHost httpHost = new HttpHost(httpClientRequest.getProxyInfo().getHostName(), httpClientRequest.getProxyInfo().getPort());
                builder.setProxy(httpHost);
            }
            requestConfig = builder.build();
        }
        return requestConfig;
    }

    /**
     * 获取指定的头信息
     *
     * @param responseHeaders 返回头集合
     * @param headName        指定的头名称
     * @return value
     */
    public static String getHeadByName(Header[] responseHeaders, String headName) {
        String headValue = "";
        for (Header responseHeader : responseHeaders) {
            if (responseHeader.getName().equals(headName)) {
                headValue = responseHeader.getValue();
                return headValue;
            }
        }
        return headValue;
    }


}
